import json
import httpx
from typing import Any, Dict, Optional, Union
from fastapi import Request
from app.music_api import get_timestamp
from app.supply import DEBUG_MODE, MAX_REDIRECTS, log
from fastapi.responses import JSONResponse, StreamingResponse
from urllib.parse import parse_qs, unquote, urlencode, urljoin, urlparse, urlunparse


# 需要流式响应的HTTP方法
STREAMING_METHODS = {"GET", "POST", "PROPFIND"}


def infer_type(value: str) -> Union[int, float, str]:
    """自动推断参数值的类型"""
    try:
        return int(value)
    except ValueError:
        try:
            return float(value)
        except ValueError:
            return value


async def handle_proxy(
    request: Request,
    return_headers: bool,
    url: str,
    method: str,
    data: Optional[Union[str, bytes]] = None,
    json_data: Optional[Dict[str, Any]] = None,
    redirect_count: int = 0,
    init_headers: Optional[Dict[str, str]] = None,
) -> Union[StreamingResponse, JSONResponse]:
    client = request.app.state.client

    if not isinstance(MAX_REDIRECTS, int) or MAX_REDIRECTS < 0:
        raise ValueError("MAX_REDIRECTS must be a positive integer")

    try:
        # 请求头处理
        headers = {
            k: v
            for k, v in request.headers.items()
            if k.lower() not in {"host", "origin", "referer", "content-length"}
        }

        if init_headers:
            headers.update(init_headers)

        if content_type := request.headers.get("content-type"):
            headers["content-type"] = content_type

        # URL参数处理
        parsed_url = urlparse(url)
        url_params = parse_qs(parsed_url.query)

        # 处理查询参数中的headers
        if headers_value := request.query_params.get("headers"):
            try:
                headers_from_query = json.loads(headers_value)
                if isinstance(headers_from_query, dict):
                    headers.update(
                        {k.lower(): v for k, v in headers_from_query.items()}
                    )
            except json.JSONDecodeError:
                pass

        # 合并并处理参数
        merged_params = {
            **url_params,
            **{
                k.lower(): v
                for k, v in request.query_params.items()
                if k.lower() not in ["url", "headers"]
            },
        }

        if "timestamp" not in merged_params:
            merged_params["timestamp"] = get_timestamp()

        # 类型推断
        for key, value in merged_params.items():
            merged_params[key] = (
                [infer_type(item) for item in value]
                if isinstance(value, list)
                else infer_type(value)
            )

        clean_url = urlunparse(parsed_url._replace(query=None))

        log("\n=== 请求详情 ===")
        log(f"目标 URL: {clean_url}")
        log(f"查询参数: {merged_params}")
        log(f"请求头: {json.dumps(dict(headers), indent=2)}")

        # 准备请求参数
        request_kwargs = {
            "url": clean_url,
            "params": merged_params,
            "headers": headers,
            "follow_redirects": False,
        }

        if method.upper() == "POST":
            if json_data is not None:
                request_kwargs["json"] = json_data
            elif data is not None:
                request_kwargs["data"] = data
            else:
                request_kwargs["content"] = await request.body()

        while True:
            # 更新关键头信息
            target_parsed = urlparse(clean_url)
            target_netloc = target_parsed.netloc or target_parsed.hostname
            if target_parsed.port:
                target_netloc = f"{target_parsed.hostname}:{target_parsed.port}"

            headers.update(
                {
                    "host": target_netloc,
                    "referer": f"{target_parsed.scheme}://{target_netloc}",
                }
            )

            response = await client.request(method, **request_kwargs)

            if DEBUG_MODE:
                log(f"响应状态: {response.status_code}")
                log(f"响应内容: {response.text[:500]}...")

            if response.status_code not in [301, 302]:
                break

            if not (redirect_location := response.headers.get("location")):
                raise Exception("Redirect location is missing in response headers")

            redirect_url = urljoin(clean_url, redirect_location)
            log(f"重定向到: {redirect_url}")

            if not urlparse(redirect_url).scheme:
                redirect_url = f"{target_parsed.scheme}://{redirect_url}"

            if (redirect_count := redirect_count + 1) >= MAX_REDIRECTS:
                raise Exception("Too many redirects")

            clean_url = redirect_url
            request_kwargs["url"] = clean_url

        # 响应处理
        response_headers = {
            k: v
            for k, v in response.headers.items()
            if k.lower() not in {"content-length", "transfer-encoding"}
        }

        if method.upper() in STREAMING_METHODS:
            return StreamingResponse(
                content=response.aiter_bytes(),
                status_code=response.status_code,
                headers=response_headers if return_headers else None,
            )
        elif method.upper() == "OPTIONS":
            allow_methods = response.headers.get("allow", "").split(", ")
            return JSONResponse(
                content={"allowed_methods": allow_methods},
                status_code=response.status_code,
                headers=response_headers if return_headers else None,
            )

    except httpx.TimeoutException as e:
        log(f"Request timeout to {clean_url}: {str(e)}")
        return JSONResponse({"error": "Request timed out"}, status_code=504)
    except httpx.ConnectError as e:
        log(f"Connection failed to {clean_url}: {str(e)}")
        return JSONResponse({"error": "Connection failed"}, status_code=503)
    except httpx.RequestError as e:
        log(f"Request error to {clean_url}: {str(e)}")
        return JSONResponse({"error": f"Request error: {str(e)}"}, status_code=400)
    except Exception as e:
        log(f"Unexpected error processing {clean_url}")
        return JSONResponse({"error": str(e)}, status_code=500)


async def get_proxy(request: Request, return_headers: bool, url: str):
    return await handle_proxy(request, return_headers, url, "GET")


async def post_proxy(request: Request, return_headers: bool, url: str):
    # 获取请求体和内容类型
    original_body = await request.body()
    content_type = request.headers.get("content-type", "").lower()

    try:
        # 根据 content-type 处理请求体
        if "application/json" in content_type:
            # 尝试解析为 JSON 数据
            json_data = json.loads(original_body)
            log(f"POST JSON data: {json_data}")
            return await handle_proxy(
                request, return_headers, url, "POST", json_data=json_data
            )

        elif "application/x-www-form-urlencoded" in content_type:
            # 尝试将原始字节串解码为字符串
            decoded_body = original_body.decode("utf-8")
            log(f"POST original_body (decoded): {decoded_body}")

            # 检查是否是 URL 编码格式
            if "=" in decoded_body:  # 判断是否包含等号（URL 编码特征）
                parsed_data = parse_qs(decoded_body)
                log(f"Parsed URL-encoded data: {parsed_data}")

                # 使用 urlencode 将解析后的数据重新编码为 URL 查询字符串
                data = urlencode(parsed_data, doseq=True)
                log(f"POST data (re-encoded): {data}")
                return await handle_proxy(
                    request, return_headers, url, "POST", data=data
                )
            else:
                # 如果不是 URL 编码格式，尝试解析为 JSON
                json_data = json.loads(decoded_body)
                log(f"Parsed JSON data: {json_data}")

                # 根据 content-type 决定如何处理数据
                if "application/x-www-form-urlencoded" in content_type:
                    data = urlencode(json_data)
                    log(f"POST data (from JSON): {data}")
                    return await handle_proxy(
                        request, return_headers, url, "POST", data=data
                    )
                else:
                    return await handle_proxy(
                        request, return_headers, url, "POST", json_data=json_data
                    )

        else:
            # 对于其他 content-type，直接传递原始数据
            log(f"POST raw data: {original_body}")
            return await handle_proxy(
                request, return_headers, url, "POST", data=original_body
            )

    except UnicodeDecodeError as e:
        # 捕获解码错误
        log(f"Failed to decode request body: {e}")
        raise ValueError("Invalid encoding in request body")

    except json.JSONDecodeError as e:
        # 捕获 JSON 解析错误
        log(f"Failed to parse JSON data: {e}")
        raise ValueError("Invalid JSON format in request body")

    except Exception as e:
        # 捕获其他异常
        log(f"Unexpected error processing POST data: {e}")
        raise ValueError(f"Failed to process request body: {e}")


async def propfind_proxy(request: Request, return_headers: bool, url: str):
    original_body = await request.body()
    return await handle_proxy(
        request, return_headers, url, "PROPFIND", data=original_body
    )


async def options_proxy(request: Request, return_headers: bool, url: str):
    return await handle_proxy(request, return_headers, url, "OPTIONS")
